3. Configuration

"설정보다 관례"를 지향하는 프레임워크에서 이런 주제를 다루는게 이상하게 보일 것이다. 하지만 설정할 것이 있긴 있기 때문에 이에 대해 짚고 넘어갈 필요가 있다.

실제로 Grails를 전혀 설정하지 않고서도 개발할 수 있다. Grails에는 내장 컨테이너와 HSQLDB라는 메모리 데이터베이스가 탑재돼 있어서 데이터베이스 설정조차 필요없다.

하지만 때가 되면 실제 데이터베이스를 사용하고 싶어질 것이고 그 방법은 이 장에서 설명한다.

3.1 Basic Configuration

일반적인 설정을 위해서 grails는 grails-app/conf/Config.groovy 라는 파일을 제공한다. 이 파일은 groovy의 ConfigSlurper 사용한다. ConfigSlurper는 순수 groovy로 만들어졌다는 것을 제외하고는 자바 프로퍼티 파일과 매우 유사하다. 따라서 적절한 자바 타입과 변수를 재사용 할 수 있다.

예를 들어, 다음과 같이 설정을 추가할 수 있다:

foo.bar.hello = "world"

그리고 나서 어플리케이션에서 두 가지 방법으로 이 설정에 접근할 수 있다. 컨트롤러나 태그라이브러리에서 사용가능한 GrailsApplication 객체를 통해서 접근하는 것이 가장 일반적이다:

assert "world" == grailsApplication.config.foo.bar.hello

두번째 방법으로 설정 객체를 참조하고 있는 ConfigurationHolder 사용할 수도 있다:

import org.codehaus.groovy.grails.commons.*
…
def config = ConfigurationHolder.config
assert "world" == config.foo.bar.hello

3.1.1 Built in options

Grails는 다음과 같은 설정 옵션을 제공한다.

3.1.2 Logging

Logging Basics

Grails는 Log4j 로그 시스템에 기반한 공통의 '설정 매커니즘'을 사용한다. grails-app/conf 디렉토리에 있는 Config.groovy를 수정하여 로깅을 설정하고 Config.groovy파일 하나로 development, test, production의 각 환경에 맞게 로깅 구성을 따로 정할 수 있다. Grails는 Config.groovy파일을 처리해서 web-app/WEB-INF/classes 디렉토리 안에 알맞은 log4j.properties 파일을 생성한다.

Grails의 일반적인 Log4j 설정은 다음과 같다:

log4j {
    appender.stdout = "org.apache.log4j.ConsoleAppender"
	appender.'stdout.layout'="org.apache.log4j.PatternLayout"
    rootLogger="error,stdout"
    logger {
        grails="info,stdout"
        org {
            grails.spring="info,stdout"
            codehaus.groovy.grails.web="info,stdout"
            codehaus.groovy.grails.commons="info,stdout"
            …
        }
}
}

표준 Log4j 프로퍼티 파일 스타일을 선호한다면 groovy 멀티라인 문자열을 사용할 수 있다:

log4j = '''
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# ...remaining configuration
'''

여기에 유용한 logger를 소개한다:

Full stacktraces

예외가 발생 했을 때 자바와 Groovy 내부에서부터 스택을 트레이스하면 심한 노이즈가 있을 수 있다. Grails는 보통 관련이 없는 것들을 걸러내고 중요하지 않은 Grails/Groovy 클래스 패키지만을 Trace하도록 제한한다. Grails는 보통 그러한 관련이 없는 것들을 필터링 해주고, 핵심 Grails/Groovy 클래스 패키지가 아닌 것으로 Trace를 제한하고 있다.

예외가 발생하면, 전체 추적 내용은 항상 StackTrace 로거에 씌여진다. StackTrace는 stacktrace.log 파일에 기록하지만 Congfig.groovy파일을 이용해서 원하는 곳에 기록하도록 할 수 있다. 예를 들어 만약에 표준 출력으로 전체 추적 내용을 기록하고 싶다면 다음과 같은 설정을:

StackTrace="error,errors"

다음과 같이 바꾼다:

StackTrace="error,stdout"

grails.full.stacktrace VM 프로퍼티을 true로 설정을 해서 스택 트레이스 필터링을 끌 수 있다.

grails -Dgrails.full.stacktrace=true run-app

Logging by Convention

어플리케이션의 모든 아티펙트에는 log 프로퍼티을 동적으로 추가할 수 있다. 아티펙트는 도메인 클래스, 컨트롤러, 태그 라이브러리 등을 말한다. 이것의 사용법은 다음과 같다:

def foo = "bar"
log.debug "The value of foo is $foo"

로그의 이름은 grails.app.<아티펙트 타입>.ClassName 과 같은 관례에 따른다. 다음은 다양한 Grails 아티펙트에 로그를 설정하는 예이다:

# Set level for all application artefacts
log4j.logger.grails.app="info, stdout"

# Set for a specific controller log4j.logger.grails.app.controller.YourController="debug, stdout"

# Set for a specific domain class log4j.logger.grails.app.domain.Book="debug, stdout"

# Set for a specific taglib log4j.logger.grails.app.tagLib.FancyAjax="debug, stdout"

# Set for all taglibs log4j.logger.grails.app.tagLib="info, stdout"

아티펙트들의 이름은 관례에 따른다. 다음은 일반적인 아티펙트들의 목록이다.

3.2 Environments

Per Environment Configuration (환경별 설정)

Grails는 환경마다 설정할 수 있다. grails-app/conf디렉토리에 있는 Config.groovy파일과, DataSource.groovy파일이 환경단위로 설정할 수 있도록 돕는다. 각 파일들은 ConfigSlurper 의 문법을 따른다. 다음은 Grails에서 제공하는 기본 DataSource의 정의를 예로 든 것이다.

dataSource {
    pooled = false                          
    driverClassName = "org.hsqldb.jdbcDriver"	
    username = "sa"
    password = ""				
}
environments {
    development {
        dataSource {
            dbCreate = "create-drop" // one of 'create', 'createeate-drop','update'
            url = "jdbc:hsqldb:mem:devDB"
        }
    }   
    test {
        dataSource {
            dbCreate = "update"
            url = "jdbc:hsqldb:mem:testDb"
        }
    }   
    production {
        dataSource {
            dbCreate = "update"
            url = "jdbc:hsqldb:file:prodDb;shutdown=true"
        }
    }
}

최상위 수준에 정의한 설정을 기억해야 한다. environments 블럭에는 DataSource의 url 프로퍼티과 dbCreate를 위해 환경마다 블럭을 정의하였다. 이 문법은 Config.groovy에서도 사용된다.

Packaging and Running for Different Environments (다양한 환경을 위한 패키징과 실행)

Grails는 특정 환경단위로 명령을 실행할 수 있도록 지원한다. 그 형식은 다음과 같다:

grails [environment] [command name]

또한 Grails에는 dev, prod, test이라는 미리 만들어진 환경이 3개있다. dev, prod, test는 각각 개발(development), 서비스(production), 테스트(test)를 위한 것이다. 예를 들어 테스트 환경에서 WAR를 만들기 위해서는 명령어를 다음과 같이 사용해야 한다.

grails test war

만약에 또 다른 환경을 가지고 있다면 grails.env 변수를 통해서 그 환경을 지정할 수 있다. 아래는 그 예이다:

grails -Dgrails.env=UAT run-app

Programmatic Environment Detection (프로그램에 의한 환경 검사)

GrailsUtil 클래스를 Gant 스크립트나 부트스트랩 클래스 같은 곳에 사용함으로써 환경을 찾아낼 수 있다:

import grails.util.GrailsUtil

...

switch(GrailsUtil.environment) { case "development": configureForDevelopment() break case "production": configureForProduction() break }

3.3 The DataSource

Grails는 자바로 만들어졌기 때문에 data source를 설정하려면 JDBC에 대한 약간의 지식이 필요하다(자바 데이터베이스 접속과는 관계없는 지식).

만약 HSQLDB외에 다른 DB를 사용하고 싶다면, 필수적으로 JDBC드라이버가 필요하다. MySQL을 예로 들자면 Connector/J 가 필요하다.

드라이버는 일반적으로 JAR 아카이브 형대로 배포된다. JAR파일을 프로젝트의 lib 디렉토리에 넣어라.

일단 grails-app/conf/DataSource.groovy 에 있는 DataSource descriptor파일에 JAR에 대한 정보가 있어야 한다. 그 파일은 다음과 같은 정보들은 DataSource에 대한 정의를 담고 있다.

MySQL에 대한 일반적인 설정은 아마도 다음과 같을 것이다.

dataSource {
	pooled = true
	dbCreate = "update"
	url = "jdbc:mysql://localhost/yourDB"
	driverClassName = "com.mysql.jdbc.Driver"
	username = "yourUser"
	password = "yourPassword"	
}

DataSource를 설정할 때에는 타입이나 def 키워드를 사용하지 않는다. 왜냐하면 Groovy는 그러한 문법들을 지역변수를 정의한 것으로 보고 처리하지 않을 것이기 때문이다. 예를 들어 다음과 같은 경우는 잘못된 것이다.

dataSource {
	boolean pooled = true // type declaration results in local variable
	…
}

3.3.1 DataSources and Environments

이전의 구성 예는 모든 환경(서비스, 테스트, 개발등)이 같다고 가정하였다.

하지만, Grails는 DataSource를 "환경 별로(environment aware)" 정의할 수 있다다. 따라서 다음과 같이 할 수 있다.

dataSource {
	// common settings here
}                     
environments {
  production {
     dataSource {
          url = "jdbc:mysql://liveip.com/liveDb"					
     }			
  }
}

3.3.2 JNDI DataSources

많은 Java EE 컨테이너는 보통 JNDI(Java Naming and Directory Interface )의 DataSource 인스턴스를 제공한다. 그래서 DataSource를 통해서 JNDI를 사용해야 할 때도 있다.

아래와 같이 JNDI data source를 정의할 수 있다.

dataSource {
    jndiName = "java:comp/env/myDataSource"
}

JNDI 이름의 형태는 컨테이너 마다 다를지도 모르지만, DataSource를 정의하는 방법은 똑같다.

3.3.3 Automatic Database Migration

DataSource를 정의할 때 dbCreate 프로퍼티은 중요하다. 그 이유는 Grails가 런타임에 GORM 클래스를 이용해 자동적으로 database 테이블를 생성하는 것을 결정하기 때문이다. 옵션들은 다음과 같다.

create-drop과 create 둘 다 이미 있는 데이터를 파괴하므로 사용에 주의해야 한다.

개발 환경 에서의 dbCreate의 기본값은 “create-drop”이다.

dataSource {
	dbCreate = "create-drop" // one of 'create', 'create-drop','update'
}

이것은 어플리케이션을 재시작 할 때마다 자동으로 db 테이블을 삭제하고 다시 생성한다. 확실히 이것은 실제 서비스에는 필요없다.

비록 Grails가 지금은 Rails 스타일의 이전작업(Migrations)을 지원하고 있지 않지만, 현재 비슷한 기능의 플러그인이 두 개 있다. LiquiBase 플러그인과 DbMigrate 플러그인을 list-plugins 명령어로 사용할 수 있다.

3.4 Externalized Configuration

기본 설정 파일인 grails-app/conf 에 있는 Config.groovy 는 주요 상황에서는 꽤 괜찮다. 하지만 메인 어플리케이션 구조 바깥에 구성 파일을 유지하고 싶을때가 있을 수 있다. 예를 들어, 어떤 관리자들은 WAR를 설치한 후에 설정을 변경하기 위하여 WAR을 다시 만들기 싫어서 어플리케이션의 설정을 외부로 빼고 싶어한다.

이렇게 설정하는 시나리오를 위해 설정을 어플리케이션 밖에 위치 시킬 수 있다. Config.groovy 파일 안에 grails.config.locations 설정을 다음과 같이 사용하여 설정 파일의 위치를 Grails에 알린다:

grails.config.locations = [ "classpath:${appName}-config.properties",
                            "classpath:${appName}-config.groovy",
                            "file:${userHome}/.grails/${appName}-config.properties",
                            "file:${userHome}/.grails/${appName}-config.groovy"]

이 예제는 클래스패스와 다른 경로인 USER_HOME 에 위치한 설정파일(Java 프로퍼티 파일과 ConfigSlurper 설정 모두)을 로드한다.

모든 설정은 결국 GrailsApplication 객체의 config 프로퍼티로 합쳐지기 때문에 이를 이용해 설정에 접근할 수 있다.

또 Grails는 Spring의 프로퍼티 플레이스홀더(placeholder)와 프로퍼티를 오버라이드하는 configurer 개념도 지원한다. 자세한 정보는 Grails와 Spring 에 대한 장에 있다.

3.5 Versioning

Versioning Basics(버전 기초)

Grails에는 어플리케이션 버전을 관리하는 기능이 내장되어 있다. create-app 를 이용해서 어플리케이션을 처음 만들었을 때의 버전은 0.1이 된다. 버전은 프로젝트의 루트에 있는 application.properties 라는 어플리케이션 메타 데이터에 저장이 된다.

어플리케이션의 버전을 바꾸기 위해서는, set-version명령어를 사용한다.

grails set-version 0.2

버전은 war 명령어뿐만 아니라 많은 명령어에서 사용된다. war 명령어를 실행하면 생성되는 WAR파일의 끝에 어플리케이션의 버전을 덧붙혀진다.

Detecting Versions at Runtime(런타임에 버전 알아내기)

런타임에 어플리케이션의 버전을 알아낼 수 있다. Grails에서 제공하는 GrailsApplication 클래스를 통해 어플케이션의 메타데이터에 접근한다. 예를 들어 컨트롤러 에는 다음과 같이 사용할 수 있는 grailsApplication 변수가 이미 있다:

def version = grailsApplication.metadata['app.version']

만약 Grails의 버전을 알고 싶다면 다음과 같이 사용할 수 있다.

def grailsVersion = grailsApplication.metadata['app.grails.version']

또는 GrailsUtil 클래스를 사용할 수도 있다:

import grails.util.*
def grailsVersion = GrailsUtil.grailsVersion

3.6 Deployment

빠른 설치

실제 서비스(production) 환경에서는 Grails 어플리케이션들이 언제나 WAR파일로 설치된다. Grails 어플리케이션을 절대로 run-app명령어를 사용해서 설치하면 안된다. 왜냐하면, 그 명령어는 개발하는 동안 Grails를 auto-reloading을 하도록 구성하기 때문이다.

하지만 run-war명령어를 이용해 다음과 같이 쉽고 빠르게 Grails 어플리케이션을 실행 할 수 있다.

grails prod run-war

80번 포트로 실행하도록 하는 방법이다.

grails -Dserver.port=80 prod run-war

그러면 WAR 파일이 생성되고 서비스production 환경의 WAR 파일은 Jetty를 이용해서 실행된다.

WAR 설치

이전에 WAR 설치에 대해 언급했었다. war 명령어는 WAR 파일을 만들기 위한 편의를 제공한다. WAR 파일은 다양한 Java EE 호환 컨테이너에 설치된다.

WAR 파일을 만들었다면 다음은 WAR 파일을 적당한 디렉토리에 넣어야 한다. 만악 컨테이너가 핫 디플로이먼트hot deployment를 지원한다면 자동으로 리로드될 것이고 그렇지 않으면 컨테이너를 재기동해야 할 것이다.

예를 들어 아파치 톰캣에서는 TOMCAT_HOME/webapps 디렉토리에 WAR를 놓고 컨테이너를 재시작하면 된다.

또 다른 컨테이너(특히 상업용)에서는 보통 웹 인터페이스를 제공한다. WAR파일을 웹 인터페이스로 실행중인 컨테이너에 올리고, 설치할 수 있다. 자세한 것은 해당 컨테이너에 대한 문서를 참조하고 설명대로 따라해야 한다.

WAR 파일 생성 수정하기

만약 WAR 생성과정을 변경해야 한다면 다양한 방법으로 변경할 수 있다. 만약 간단하게 WAR가 생성되는 장소를 변경하고 싶다면 다음과 같이 하면된다:

grails war /my/container/path

위 방법 대신에 Config.groovy파일의 grails.war.destFile 옵션을 설정해도 된다:

grails.war.destFile=/my/container/path

만약에 WAR 파일을 생성하는 동안 의존성들을 수정해야 한다면(예를들어 컨테이너가 공유 라이브러리를 지원한다면) 다음과 같이 특정한 Ant fileset을 grails.war.dependencies 옵션에 명시할 수 있다.

grails.war.dependencies = {
	fileset(dir:"/my/libs", includes:"*.jar")
}

좀 더 유연하게 grails.war.resources를 설정할 수 있다. 이 설정에 WAR 파일을 어디에 생성할지 명시할 수 있다.

grails.war.resources = { warLocation ->
	copy(todir:"$warLocation/WEB-INF") {
		fileset(dir:"/my/configs", includes:"*.xml")
	}
}